package cn.com.bsoft.mobile.utils
import java.text.ParseException
import java.util.ArrayList
import java.util.zip.ZipEntry
import java.util.zip.ZipInputStream

import javax.xml.parsers.ParserConfigurationException

import org.xml.sax.SAXException

import com.dd.plist.NSArray
import com.dd.plist.NSDictionary
import com.dd.plist.PropertyListFormatException
import com.dd.plist.PropertyListParser
import java.io.*

class IPAInfo {
    var minimumOSVersion: String? = null
    var bundleVersionString: String? = null
    var buildNumber: String? = null
    var bundleName: String? = null
    var requiredDeviceCapabilities: String? = null
    var bundleIcon: ByteArray? = null
    var platformVersion: String? = null
    private var iPadSupport: Boolean? = null
    private var iPhoneSupport: Boolean? = null
    var bundleIdentifier: String? = null
    var provisioningProfileName: String? = null
    var provisioningProfileCreationDate: String? = null
    var provisioningProfileExpirationDate: String? = null
    var provisioningProfileDevices: MutableList<String>? = null
    var teamIdentifier: String? = null
    var teamName: String? = null
    var infoPlistFile: ByteArray? = null
    var mobileProvisionFile: ByteArray? = null
    var fileSize: Long = 0
    var bundleIconFileName: String? = null
    fun getiPadSupport(): Boolean? {
        return iPadSupport
    }

    fun setiPadSupport(iPadSupport: Boolean?) {
        this.iPadSupport = iPadSupport
    }

    fun getiPhoneSupport(): Boolean? {
        return iPhoneSupport
    }

    fun setiPhoneSupport(iPhoneSupport: Boolean?) {
        this.iPhoneSupport = iPhoneSupport
    }
}

class IPAReader(private val input:InputStream) {

    fun getLastIconFileName(dict: NSDictionary, identifier: String): String? {
        val primaryIcon = dict[identifier] as NSDictionary
        val iconFiles = primaryIcon["CFBundlePrimaryIcon"] as NSDictionary?
        val files = (iconFiles?.get("CFBundleIconFiles") as NSArray?)?.array

        var name: String? = null

        files?.forEach {
            name = it.toString()
        }

        return name
    }

    @Throws(IOException::class, PropertyListFormatException::class, ParseException::class, ParserConfigurationException::class, SAXException::class)
    fun parse(): IPAInfo? {
        val info = IPAInfo()
        val bytes = input.readBytes()
        info.fileSize = bytes.size.toLong()

        var zipIn = ZipInputStream(ByteArrayInputStream(bytes))
        var entry: ZipEntry? = zipIn.nextEntry

        while (entry != null) {
            if (entry.name.endsWith(".app/embedded.mobileprovision")) {
                val stream = readFileToMemory(zipIn)
                info.mobileProvisionFile = stream.toByteArray()
                val plist = getPlistFromMobileProvisionFile(stream)

                if (plist != null) {
                    val rootDict = PropertyListParser.parse(plist.toByteArray()) as NSDictionary
                    info.provisioningProfileCreationDate = rootDict["CreationDate"].toString()
                    info.provisioningProfileExpirationDate = rootDict["ExpirationDate"].toString()
                    info.provisioningProfileName = rootDict["Name"].toString()
                    info.teamIdentifier = rootDict["TeamIdentifier"].toString()
                    info.teamName = rootDict["TeamName"].toString()

                    val devices = (rootDict["ProvisionedDevices"] as NSArray?)?.array

                    val list: MutableList<String> = ArrayList()
                    if (devices!=null) {
                        for (device in devices) {
                            list.add(device.toString())
                        }
                    }
                    info.provisioningProfileDevices = list
                } else {
                    return null
                }
            } else if (entry.name.endsWith(".app/Info.plist")) {
                info.infoPlistFile = readFileToMemory(zipIn).toByteArray()
                val rootDict = PropertyListParser.parse(info.infoPlistFile!!) as NSDictionary

                info.minimumOSVersion = rootDict["MinimumOSVersion"].toString()
                info.bundleName = rootDict["CFBundleName"].toString()
                info.bundleVersionString = rootDict["CFBundleShortVersionString"].toString()
                info.bundleIdentifier = rootDict["CFBundleIdentifier"].toString()
                info.buildNumber = rootDict["CFBundleVersion"].toString()
                info.platformVersion = rootDict["DTPlatformVersion"].toString()

                if (rootDict.containsKey("UIRequiredDeviceCapabilities")) {
                    val o = (rootDict["UIRequiredDeviceCapabilities"] as NSArray).array

                    if (o.size > 0) {
                        info.requiredDeviceCapabilities = o[0].toString()
                    }
                }

                if (rootDict.containsKey("CFBundleIcons")) {
                    info.setiPhoneSupport(true)

                    info.bundleIconFileName = this.getLastIconFileName(rootDict, "CFBundleIcons")
                } else {
                    info.setiPhoneSupport(false)
                }

                if (rootDict.containsKey("CFBundleIcons~ipad")) {
                    info.setiPadSupport(true)

                    info.bundleIconFileName = this.getLastIconFileName(rootDict, "CFBundleIcons~ipad")
                } else {
                    info.setiPadSupport(false)
                }
            }

            zipIn.closeEntry()
            entry = zipIn.nextEntry
        }
        zipIn.close()

        if (info.bundleIconFileName != null) {
            zipIn = ZipInputStream(ByteArrayInputStream(bytes))
            entry = zipIn.nextEntry

            while (entry != null) {
                if (entry.name.endsWith("/" + info.bundleIconFileName!!)) {
                    info.bundleIcon = readFileToMemory(zipIn).toByteArray()
                }

                zipIn.closeEntry()
                entry = zipIn.nextEntry
            }
            zipIn.close()

        }

        return if (info.bundleIdentifier != null) {
            info
        } else null

    }

    @Throws(UnsupportedEncodingException::class)
    fun getPlistFromMobileProvisionFile(stream: ByteArrayOutputStream): String? {
        var s = stream.toString("UTF-8")

        var i = s.indexOf("<plist version=\"1.0\">")

        if (i >= 0) {
            s = s.substring(i)

            i = s.indexOf("</plist>")

            if (i >= 0) {
                return s.substring(0, i + "</plist>".length)
            }
        }

        return null
    }

    @Throws(IOException::class)
    fun readFileToMemory(zipIn: ZipInputStream): ByteArrayOutputStream {
        val bos = ByteArrayOutputStream()
        bos.write(zipIn.readBytes())

        return bos
    }
}